package cn.xiangxu.com.webclient_rest_client.proxy;

import com.sun.security.ntlm.Server;
import org.reflections.Reflections;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.boot.autoconfigure.data.rest.RepositoryRestProperties;
import org.springframework.stereotype.Component;

import java.lang.annotation.Annotation;
import java.util.Set;

/**
 * @author 王杨帅
 * @create 2018-08-23 22:50
 * @desc 动态注册Bean的工具类
 **/
@Component
public class DynamicRegisterBeanUtil implements BeanFactoryPostProcessor {

    /**
     * 动态注册Bean所需对象
     */
    private DefaultListableBeanFactory defaultListableBeanFactory;

//    @Value("${reflectionsScanPackage}")
//    private String reflectionsScanPackage;
//    @Autowired
//    private ReflectionsProperties reflectionsProperties;
//    private String scanPackage = "cn.xiangxu.com.webclient_rest_client.rest_server_api";

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {

        this.defaultListableBeanFactory = (DefaultListableBeanFactory) configurableListableBeanFactory;

//        System.out.println("动态注册Bean的工具类中获取到的扫描路径为：" + scanPackage);

//        01 获取指定路径下的所有类类型
        Reflections reflections = new Reflections("cn.xiangxu.com.webclient_rest_client.rest_server_api");

//        02 获取标注了@Rest注解的类类型
        Set<Class<?>> typesAnnotatedWithRestApi = reflections.getTypesAnnotatedWith(RestApi.class);

//        03 为标注了@RestApi 注解的所有类类型创建代理类并将该代理类注册到IOC容器中去
        for (Class cls : typesAnnotatedWithRestApi) {
//            System.out.println(cls.getSimpleName());
            // 创建代理类的BeanDefinition并将其注册到IOC容器中去
            try {
                createProxyClass(cls);
            } catch (NoSuchMethodException e) {
                e.printStackTrace();
            }
        }

    }

    /**
     * 创建代理类并将代理类注册到IOC容器中去
     * @param cls 需要创建代理类的的类类型
     * @throws NoSuchMethodException
     */
    private void createProxyClass(Class cls) throws NoSuchMethodException {
        // 一、创建代理类对象
        MyInvocationHandler handler = getMyInvocationHandler();

        // 封装服务器信息到处理中
        handler.init(extractServerInfo(cls));

        // 二、获取代理类的Bean定义
        BeanDefinition proxyBeanDefinition = getProxyBeanDefinition(cls, handler);

        // 三、将代理类的Bean信息注册到Spring容器中
        registerBeanDefinition(cls, proxyBeanDefinition);
    }

    /**
     * 根据类类型获取服务器信息
     * @param cls
     */
    private ServerInfo extractServerInfo(Class cls) {

        ServerInfo serverInfo = new ServerInfo();

        RestApi annotation = (RestApi) cls.getAnnotation(RestApi.class);

        serverInfo.setBaseUrl(annotation.value());

        return serverInfo;

    }

    /**
     * 将代理类的Bean信息注册到Spring容器中：Bean名称为接口名
     * @param cls
     * @param proxyBeanDefinition
     */
    private void registerBeanDefinition(Class cls, BeanDefinition proxyBeanDefinition) {
        this.defaultListableBeanFactory.registerBeanDefinition(cls.getSimpleName(), proxyBeanDefinition);
    }

    /**
     * 获取JDK代理类的Bean定义：根据目标接口的类型和处理器实例获取目标类接口的JDK代理类的Bean定义
     * @param cls
     * @param handler
     * @return
     * @throws NoSuchMethodException
     */
    private BeanDefinition getProxyBeanDefinition(Class cls, MyInvocationHandler handler) throws NoSuchMethodException {
        // 获取JDK代理类的类型
        Class<?> jdkDynamicProxyClass = getJDKDynamicProxyClass(cls, handler);

        // 获取JDK代理类的Bean定义
        BeanDefinition jdkBeanDefinition = getJDKBeanDefinition(jdkDynamicProxyClass, handler);

        return jdkBeanDefinition;
    }

    /**
     * 获取JDK代理类的Bean定义：根据JDK代理类的类型和处理类实例创建JDK代理类的Bean定义
     * @param jdkDynamicProxyClass
     * @param handler
     */
    private BeanDefinition getJDKBeanDefinition(Class<?> jdkDynamicProxyClass, MyInvocationHandler handler) {
        AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder
                .genericBeanDefinition(jdkDynamicProxyClass)
                .addConstructorArgValue(handler)
                .getBeanDefinition();
        beanDefinition.setAutowireCandidate(true);
        return beanDefinition;
    }

    /**
     * 获取JDK代理类的类类型
     * @param cls
     * @param handler
     */
    private Class<?> getJDKDynamicProxyClass(Class cls, MyInvocationHandler handler) throws NoSuchMethodException {
        JdkProxyCreator jdkProxyCreator = new JdkProxyCreator(new Class[]{cls}, handler);
        return jdkProxyCreator.getProxyClassInfo();
    }

    /**
     * 获取创建代理时所需的处理类
     * @return
     */
    private MyInvocationHandler getMyInvocationHandler() {
        return new MyInvocationHandler();
    }
}

